<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="Generator" content="EditPlus®">
    <meta name="Author" content="">
    <meta name="Keywords" content="">
    <meta name="Description" content="">
    <title>在canvas中绘制和拖动</title>
    <style type="text/css">
        * {
            margin: 0px;
            padding: 0px;
        }

        body {
            display: grid;
            justify-content: center;
        }

        .input {
            margin-left: calc(45%);
            margin-top: 10px;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
    <input type="color" class="input" />
    <canvas style="background: #e2dfdf;"></canvas>
    <script type="text/javascript">
        const colorPicker = document.querySelector('input');
        const cvs = document.querySelector('canvas');
        const ctx = cvs.getContext("2d");

        const dpr=window.devicePixelRatio;
        function init() {
            const w = 500, h = 300;
            cvs.width = w * dpr;
            cvs.height = h * dpr;
            cvs.style.width = w + 'px';
            cvs.style.height = h + 'px';
        }
        init();

        const shapes = [];

        class Rectangle {
            constructor(color, startX, startY) {
                this.color = color;
                this.startX = startX;
                this.startY = startY;
                this.endX = startX;
                this.endY = startY;
            }

            get minX() {
                return Math.min(this.startX, this.endX);
            }

            get minY() {
                return Math.min(this.startY, this.endY);
            }

            get maxX() {
                return Math.max(this.startX, this.endX);

            }
            get maxY() {
                return Math.max(this.startY, this.endY);
            }

            draw() {
                ctx.beginPath();
                ctx.moveTo(this.minX * dpr, this.minY * dpr);
                ctx.lineTo(this.maxX * dpr, this.minY * dpr);
                ctx.lineTo(this.maxX * dpr, this.maxY * dpr);
                ctx.lineTo(this.minX * dpr, this.maxY * dpr);
                ctx.lineTo(this.minX * dpr, this.minY * dpr);
                ctx.fillStyle = this.color;
                ctx.fill();
                ctx.strokeStyle = '#fff';
                ctx.lineCap = 'square';
                ctx.lineWidth = 3 * dpr;
                ctx.stroke();
            }
        }

        cvs.onmousedown = (e) => {
            const bouding = cvs.getBoundingClientRect();
            const rect = new Rectangle(colorPicker.value, e.offsetX, e.offsetY);
            const shape = getShape(e.offsetX, e.offsetY);
            if (shape) {
                const { startX, startY, endX, endY } = shape;
                const mouseX = e.offsetX;
                const mouseY = e.offsetY;
                window.onmousemove = (e) => {
                    const disX = e.clientX - bouding.left - mouseX;
                    const disY = e.clientY - bouding.top - mouseY;
                    shape.startX = startX + disX;
                    shape.startY = startY + disY;
                    shape.endX = endX + disX;
                    shape.endY = endY + disY;
                }
                shapes.push(shape);
            } else {
                shapes.push(rect);
                window.onmousemove = (e) => {
                    rect.endX = e.clientX - bouding.left;
                    rect.endY = e.clientY - bouding.top;
                };
            }

            window.onmouseup = (e) => {
                window.onmouseup = null;
                window.onmousemove = null;
            }
        }

        function getShape(x, y) {
            for (let i = shapes.length - 1; i >= 0; i--) {
                if (x >= shapes[i].minX && x <= shapes[i].maxX && y >= shapes[i].minY && y <= shapes[i].maxY) {
                    return shapes[i];
                }
            }
        }

        function draw() {
            requestAnimationFrame(draw);
            ctx.clearRect(0, 0, cvs.width, cvs.height);
            for (const shape of shapes) {
                shape.draw();
            }
        }
        draw();
    </script>
</body>

</html>